﻿(*
In this chapter we will see how we can build a rendering system in XNA for the "asteroids" and "police chase" samples.

We will start with setting the initial solution right, since F# does not have the appropriate templates for interacting with XNA.
Then we will see how we can define the initial F# game that does nothing but invoking our logic.
We will add 2D rendering to this game, with the use of the SpriteBatch class;
then we will add some simple input management to be able to zoom in and around our asteroid field.

After completing the 2D renderer for the asteroids simulation, we will create a 3D renderer to draw the police chase.
We will begin by simply drawing some models for the ships and the police station, together with a moveable camera.
Then we will add some aspects such as lasers, a nebula background and some stars to make the presentation a bit more aesthetically pleasing.
Finally we will add a simple soundtrack to our final game.

Keep in mind that we are just showing how to get some basic rendering done in XNA.
By fairly complicating these samples, we could squeeze a far more cinematic behavior of our entities and a much more beautiful visual experience.
We will not do so, since this would definitely go beyond the purpose of the book;
just remember that we are barely scratching the surface of what can be achieved with these tools!

[[[
About the ( |> ) operator ( * )

In the remainder of this chapter and the next, we will often use a standard F# operator, ( |> ). 
This operator is very simple, and is defined as:
let (|>) x f = f x

its use is to make the "subject" of an operation appear before the operation itself, to enhance readability and reduce the number of parentheses.
This operator is particularly nice if, when building complex expressions, we find ourselves writing code like:
f(g(h(x)))

because it allows us to write:
x |> h |> g |> f

which is read more naturally, since the order of application is written from left to right instead of right to left.

A concrete example that uses both the ( |> ) operator and partial specialization could be:
let positive_even_plus_k k l =
  l |> List.filter (fun x -> x > 0) |> List.filter (fun x -> x % 2 = 0) |> List.map ((+) k)

which can be read very naturally.
]]]
*)
namespace Chapter6

(*
Setting up XNA and F# ( * )

We start with the configuration of the right solution for working with XNA and F# together.
For everything to work, we need Visual Studio 2010, F# and XNA 4 (should work with older versions, but we have not tried it).
While this is not in the least difficult, the small problem lies in the fact that the project templates that setup the right configuration options for XNA are C#-only.
Similar problems are using F# with WPF or ASP.Net, where there is no designer for WPF/ASP.Net and F# projects.
This kind of situation is handled by creating a C# project, usually called the host, which will simply contain the main of the program and that will delegate all functionality to the definitions inside an F# library.

We start by creating a new C# solution, according to the "Windows Game" template. Let's call the game "Game Host".
The created solution now contains the following projects:
- GameHost, which is a C# project that will compile into the executable of our game
- GameHostContent (Content), which is a custom XNA project where we will put all the content files (textures, models, songs, etc.) for our application

The GameHost project has a reference to the GameHostContent project, contained in its "Content References" folder.
The GameHost project contains a file, "Game1.cs", which we can delete immediately since its functionality will be moved to an F# project.
Now we can add an F# project, called "GameLibrary"; we add to this project references to all the Microsoft.Xna.Framework.* .Net assemblies and we add this project to the references of the GameHost project.
Now our F# library can access everything that can be done in XNA, and it can access all the content files of the application.

The final solution contains:
- the GameHost C#/XNA project, with a reference to the GameLibrary project and a Content Reference to the GameHostContent project
- the GameHostContent XNA project
- the GameLibrary F# project, with references to all the XNA assemblies
*)
module Simple =

  (*
  An Empty Game ( * )

  In this section we will build an empty XNA game that runs our simulation without doing any rendering.
  At the end of this chapter we will have seen how to create an XNA game and how we can interface this game with our F# simulation logic.

  We start by including a few XNA libraries:
  - Microsoft.Xna.Framework contains the base XNA functionalities such as the game loop, vectors, matrices, etc.
  - Microsoft.Xna.Framework.Graphics contains rendering functionailities, and it is also needed to customize the game window
  *)
  open System
  open System.Collections.Generic
  open Microsoft.Xna.Framework
  open Microsoft.Xna.Framework.Graphics
  (*
  We will use the asteroids simulation for the next few sections.
  *)
  open Chapter3

  (*
  A game in XNA is an instance of the Microsoft.Xna.Framework.Game (in short, Game) class defined in the Microsoft.Xna.Framework assembly.

  The Game class is a virtual class, that is it contains some members that we can redefine (virtual).
  This way, when we pass an instance of this class with some members redefined, it will be our members that are run by the caller according to some specified logic.

  The Game class contains four virtual members:
  - Initialize, to initialize the game logic
  - LoadContent, to load data from the Content project
  - Update, to update the game logic
  - Draw, to draw the current state of the game logic

  We use the Game class exactly as we would use a record of functions defined as:

  type Game =
    {
      Initialize    : Unit -> Unit
      LoadContent   : Unit -> Unit
      Update        : GameTime -> Unit
      Draw          : GameTime -> Unit
    }

  where the GameTime datatype contains information about the current time of the game, such as how long it has been since the game has started (the TotalGameTime member) and how long since the last call to Update (the ElapsedGameTime member).
  
  We define our datatype that redefines some members of another class as follows:
  type CLASS-NAME(PARAM-NAMES) =
    inherit OTHER-CLASS-NAME(OTHER-PARAM-NAMES)
    override this.MEMBER-TO-OVERRIDE = ...
    override this.MEMBER-TO-OVERRIDE = ...
    ...

  In particular we call our class Game2D and we will inherit from the Game class.
  Since we need to access some data from our class when initializing its internal let-bindings, we give a default name to the current instance of our class by writing "as this" right after the class name and constructor parameters.
  *)
  type Game2D() as this = 
    inherit Game()
    (*
    Right after the class declaration we can initialize some internal fields of our class.
    For example, we declare a GraphicsDeviceManager that will take care of handling the graphics card for our application.
    Since the GraphicsDeviceManager needs a Game to be constructed, we had to name our class as "this" so that whenever a Game2D is constructed it uses itself to initialize its own GraphicsDeviceManager.
    We can assign some writable fields of the GraphicsDeviceManager that determine the behavior of the created window and the graphics card.
    For example, we can specify:
    - the resolution (graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight)
    - fullscreen settings (graphics.IsFullScreen)
    and many other options.

    An important option we have to specify is the current profile. 
    XNA is split into two profiles: Reach and HiDef. Reach is a strict subset of HiDef, and it contains only those functionalities that make sense on low-end hardware such as a Windows Phone 7 or a netbook PC.
    HiDef can use all features of XNA, even the most complex shader and the highest resolution textures, but it will not run on low-end hardware.

    The GameHost project is configured to build a HiDef project in its project settings, and so our game must use the same settings here.
    If the settings are not consistent, the application will crash.
    *)
    let graphics = new GraphicsDeviceManager(this)
    do graphics.GraphicsProfile <- GraphicsProfile.HiDef

    (*
    We initialize our asteroid field by reading the value of f0, which contains the initial asteroid field with 20 asteroids:
    *)
    let mutable asteroids = SmallAsteroidFieldSimulation.f0

    (*
    The Initialize and LoadContent methods don't have anything to do (yet) since we are creating an empty game.
    Notice that, since the Game class does perform some useful work in its virtual methods, it is a good idea to let it do its job by invoking those methods with base.Method:
    *)
    override this.Initialize() = 
      base.Initialize()  
    override this.LoadContent() = 
      base.LoadContent()

    (*
    In the Update member we invoke the simulation step to create a new list of asteroids.
    *)
    override this.Update gt = 
      asteroids <- SmallAsteroidFieldSimulation.simulation_step asteroids
      base.Update gt

    (*
    In the Draw member we simply clear the screen to black.
    We will always clear the screen before rendering, since we always re-draw the entire scene.

    Notice that we will be using this.GraphicsDevice to give commands to the graphics card.
    this.GraphicsDevice is initialized and managed by the graphics object of type GraphicsDeviceManager we have seen above.
    *)
    override this.Draw gt = 
      this.GraphicsDevice.Clear(Color.Black)
      base.Draw gt
